package seismosurfer.servlets.util;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import seismosurfer.data.constants.Coordinates;
import seismosurfer.data.constants.ParameterNames;
import seismosurfer.data.constants.QueryNames;
import seismosurfer.data.constants.TopNCategories;
import seismosurfer.data.constants.UpdateConstants;
import seismosurfer.database.DB;
import seismosurfer.database.QueryDAO;
import seismosurfer.domain.Quake;
import seismosurfer.util.TimeUtil;

import com.bbn.openmap.util.Debug;

/**
 * The top events queries are time-consuming
 * so this class provides a caching mechanism. 
 *
 */
public class Cache {

    protected static final String[] keys = 
    { "GINOA-DAY", "GINOA-WEEK",
      "GINOA-MONTH", "GINOA-YEAR", 
      "NEIC-DAY", "NEIC-WEEK", 
      "NEIC-MONTH", "NEIC-YEAR" 
    };

    private static Map quakes = new HashMap(8);

    private Timer timer = new Timer(true);

    public Cache(int rowCount) {
        Collections.synchronizedMap(quakes);
        loadQuakes(rowCount);
    }

    public static synchronized List getGINOADay() {
        return new ArrayList((List) quakes.get(keys[0]));
    }

    public static synchronized List getGINOAWeek() {
        return new ArrayList((List) quakes.get(keys[1]));
    }

    public static synchronized List getGINOAMonth() {
        return new ArrayList((List) quakes.get(keys[2]));
    }

    public static synchronized List getGINOAYear() {
        return new ArrayList((List) quakes.get(keys[3]));
    }

    public static synchronized List getNEICDay() {
        return new ArrayList((List) quakes.get(keys[4]));
    }

    public static synchronized List getNEICWeek() {
        return new ArrayList((List) quakes.get(keys[5]));
    }

    public static synchronized List getNEICMonth() {
        return new ArrayList((List) quakes.get(keys[6]));
    }

    public static synchronized List getNEICYear() {
        return new ArrayList((List) quakes.get(keys[7]));
    }

    protected void loadQuakes(int rowCount) {

        Map params1 = new HashMap();
        setRowNum(params1, rowCount);
        loadGINOA(params1);

        Map params2 = new HashMap();
        setRowNum(params2, rowCount);
        loadNEIC(params2);
    }

    protected void loadGINOA(Map params) {
        setGINOACoords(params);

        loadTask(Quake.DAY, params, keys[0]);
        loadTask(Quake.WEEK, params, keys[1]);
        loadTask(Quake.MONTH, params, keys[2]);
        loadTask(Quake.YEAR, params, keys[3]);
    }

    protected void loadNEIC(Map params) {
        setNEICCoords(params);

        loadTask(Quake.DAY, params, keys[4]);
        loadTask(Quake.WEEK, params, keys[5]);
        loadTask(Quake.MONTH, params, keys[6]);
        loadTask(Quake.YEAR, params, keys[7]);
    }

    public static final int PERIOD_IN_SEC = 600;

    protected void loadTask(String interval, Map params, String key) {
        CacheTask c = new CacheTask(interval, params, key);
        timer.schedule(c, 0, TimeUtil.toMillis(PERIOD_IN_SEC));
    }

    protected void setRowNum(Map parameters, int rowCount) {
        parameters.put(ParameterNames.ROWNUM, new Integer(rowCount));
        parameters.put(ParameterNames.ORDERBY, TopNCategories.STRONG);
    }

    protected void setGINOACoords(Map parameters) {
        parameters.put(ParameterNames.LONG_MIN, new Double(
                Coordinates.GREECE_LONG_MIN));
        parameters.put(ParameterNames.LONG_MAX, new Double(
                Coordinates.GREECE_LONG_MAX));
        parameters.put(ParameterNames.LAT_MIN, new Double(
                Coordinates.GREECE_LAT_MIN));
        parameters.put(ParameterNames.LAT_MAX, new Double(
                Coordinates.GREECE_LAT_MAX));

        parameters.put(ParameterNames.SOURCE,
                UpdateConstants.GI_NOA_SOURCE_NAME);
    }

    protected void setNEICCoords(Map parameters) {
        parameters.put(ParameterNames.LONG_MIN, new Double(
                Coordinates.WORLD_LONG_MIN));
        parameters.put(ParameterNames.LONG_MAX, new Double(
                Coordinates.WORLD_LONG_MAX));
        parameters.put(ParameterNames.LAT_MIN, new Double(
                Coordinates.WORLD_LAT_MIN));
        parameters.put(ParameterNames.LAT_MAX, new Double(
                Coordinates.WORLD_LAT_MAX));

        parameters.put(ParameterNames.SOURCE, UpdateConstants.NEIC_SOURCE_NAME);
    }

    class CacheTask extends TimerTask {

        private List result = null;

        private Map parameters = null;

        private String interval;

        private String key;

        public CacheTask(String interval, Map params, String key) {
            this.interval = interval;
            parameters = params;
            this.key = key;

            Debug.output("Interval:" + interval);
            Debug.output("Key:" + key);
        }

        public void run() {
            Debug.output("Running query...");
            Debug.output("Cache task starting at: "
                    + TimeUtil.toText(System.currentTimeMillis()));
            DB.openConnection();

            long timeMin = 0;
            long timeMax = 0;

            Calendar cal = Calendar.getInstance();

            timeMax = cal.getTimeInMillis();

            if (interval.equals(Quake.DAY)) {

                cal.roll(Calendar.DATE, false);
            } else if (interval.equals(Quake.WEEK)) {

                cal.roll(Calendar.WEEK_OF_YEAR, false);
            } else if (interval.equals(Quake.MONTH)) {

                cal.roll(Calendar.MONTH, false);
            } else if (interval.equals(Quake.YEAR)) {

                cal.roll(Calendar.YEAR, false);
            }

            timeMin = cal.getTimeInMillis();

            parameters.put(ParameterNames.TIME_MIN, new Long(timeMin));
            parameters.put(ParameterNames.TIME_MAX, new Long(timeMax));

            Debug.output(parameters.toString());

            QueryDAO query = new QueryDAO();
            result = query.getQuakeResults(QueryNames.TOP_N, parameters,
                    DB.NO_LIMIT);

            synchronized (quakes) {
                quakes.put(key, result);
            }

            Debug.output("Cache task ending at:"
                    + TimeUtil.toText(System.currentTimeMillis()));

            DB.closeConnection();
        }
    }
}